/*
*
* ████████╗░█████╗░███████╗██████╗░░█████╗░
* ╚══██╔══╝██╔══██╗██╔════╝██╔══██╗██╔══██╗
* ░░░██║░░░██║░░██║█████╗░░██████╦╝███████║
* ░░░██║░░░██║░░██║██╔══╝░░██╔══██╗██╔══██║
* ░░░██║░░░╚█████╔╝██║░░░░░██████╦╝██║░░██║
* ░░░╚═╝░░░░╚════╝░╚═╝░░░░░╚═════╝░╚═╝░░╚═╝
* 
*  :: 九方通逊-Henry ::   Copyright©2020 tofba.com  
* 
*/
package com.lmexpress.service.impl;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.lmexpress.enums.ApiCredential;
import com.lmexpress.enums.ApiOrderError;
import com.lmexpress.enums.LabelFormat;
import com.lmexpress.enums.LabelSize;
import com.lmexpress.enums.LabelSuffix;
import com.lmexpress.orm.eub.Item;
import com.lmexpress.orm.eub.LogisticsInterface;
import com.lmexpress.orm.eub.Receiver;
import com.lmexpress.orm.eub.Sender;
import com.lmexpress.orm.openapi.ExpressOrderApi;
import com.lmexpress.orm.openapi.OrderInvoice;
import com.lmexpress.orm.rest.ApiLabelResponse;
import com.lmexpress.orm.rest.ApiOrderResponse;
import com.lmexpress.service.ApiService;
import com.lmexpress.util.ApiUtil;
import com.lmexpress.util.HexUtil;
import com.lmexpress.util.HttpClientPoolUtil;
import com.lmexpress.util.HttpClientResult;
import com.lmexpress.util.PropertiesUtil;

/**
 * 
 * <EUB>
 * 
 * @author Henry(fba02)
 * @version [版本号, 2020-12-09]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class EubClient implements ApiService {
	private Logger LOGGER = LoggerFactory.getLogger(EubClient.class);
	private final static String DEFAULT_TRANSFER_TYPE = "HK";
	private final static String BATCH_NO_FORMAT = "yyyyMMdd";

	@Override
	public ApiOrderResponse preProcessCreateOrder(ExpressOrderApi expressOrderApi) {
		return null;
	}

	@Override
	public String mapping(ExpressOrderApi expressOrderApi) {
		LogisticsInterface logisticsInterface = new LogisticsInterface();
		Map<String, String> apiCredentials = expressOrderApi.getApiCredentials();
		logisticsInterface.setCreated_time(ApiUtil.getCurrentDateTime());
		logisticsInterface.setSender_no(apiCredentials.get(ApiCredential.EUB_SENDER_NO.getKey()));
		logisticsInterface.setMailType(apiCredentials.get(ApiCredential.EUB_MAIL_SIGN.getKey()));
		logisticsInterface.setWh_code(apiCredentials.get(ApiCredential.EUB_WH_CODE.getKey()));
		logisticsInterface.setLogistics_order_no(expressOrderApi.getCustomerInvoiceCode());

		Date date = new Date();
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(BATCH_NO_FORMAT);
		String batch_no = simpleDateFormat.format(date);
		logisticsInterface.setBatch_no(batch_no);

		logisticsInterface.setBiz_product_no(apiCredentials.get(ApiCredential.EUB_BIZ_PRODUCT_NO.getKey()));
		Double weight = expressOrderApi.getWeight();
		Integer interWeight = 0;
		logisticsInterface.setVolume(null);
		String currency = StringUtils.isBlank(expressOrderApi.getCurrencyCode()) ? DEFAULT_CURRENCY : expressOrderApi.getCurrencyCode();
		logisticsInterface.setPostage_currency(currency);
		logisticsInterface.setDeclare_curr_code(currency);
		logisticsInterface.setContents_total_value(null);
		logisticsInterface.setTransfer_type(DEFAULT_TRANSFER_TYPE);
		String thirdPartyProductCode = expressOrderApi.getThirdPartyProductCode();
		logisticsInterface.setBattery_flag(thirdPartyProductCode);
		logisticsInterface.setPickup_notes(expressOrderApi.getCustomerRemark());
		logisticsInterface.setInsurance_flag(apiCredentials.get(ApiCredential.EUB_INSURANCE_FLAG.getKey()));
		logisticsInterface.setInsurance_amount(expressOrderApi.getInsuranceAmount());
		logisticsInterface.setUndelivery_option(apiCredentials.get(ApiCredential.EUB_UNDELIVERY_OPTION.getKey()));
		logisticsInterface.setForecastshut(apiCredentials.get(ApiCredential.EUB_FORECASTSHUT.getKey()));
		logisticsInterface.setMail_sign(apiCredentials.get(ApiCredential.EUB_MAIL_SIGN.getKey()));
		Sender sender = new Sender();
		if (DEFAULT_SHIPPER.equals(ApiCredential.EUB_DEFAULT_SHIPPER.getKey())) {
			sender.setCompany(PropertiesUtil.getProperty("lm.shipper.company"));
			sender.setProvince(PropertiesUtil.getProperty("lm.shipper.province"));
			sender.setCity(PropertiesUtil.getProperty("lm.shipper.city"));
			sender.setAddress(PropertiesUtil.getProperty("lm.shipper.address_all"));
			sender.setPost_code(PropertiesUtil.getProperty("lm.shipper.zipcode"));
			sender.setName(PropertiesUtil.getProperty("lm.shipper.name"));
			sender.setLinker(PropertiesUtil.getProperty("lm.shipper.name"));
			sender.setPhone(PropertiesUtil.getProperty("lm.shipper.phone"));
		} else {
			sender.setName(expressOrderApi.getShipperName());
			sender.setCompany(expressOrderApi.getShipperCompanyName());
			sender.setPost_code(expressOrderApi.getShipperPostCode());
			sender.setPhone(expressOrderApi.getShipperTelephone());
			sender.setProvince(expressOrderApi.getShipperState());
			sender.setCity(expressOrderApi.getShipperCity());
			sender.setLinker(expressOrderApi.getShipperName());
			StringBuffer addressBuffer = new StringBuffer();
			String shipperAddress1 = expressOrderApi.getShipperAddress1();
			String shipperAddress2 = expressOrderApi.getShipperAddress2();
			String shipperAddress3 = expressOrderApi.getShipperAddress3();
			if (StringUtils.isNotBlank(shipperAddress1)) {
				addressBuffer.append(shipperAddress1).append(",");
			}
			if (StringUtils.isNotBlank(shipperAddress2)) {
				addressBuffer.append(shipperAddress2).append(",");
			}
			if (StringUtils.isNotBlank(shipperAddress3)) {
				addressBuffer.append(shipperAddress3).append(",");
			}
			sender.setAddress(addressBuffer.toString());
		}
		Receiver receiver = new Receiver();
		receiver.setName(expressOrderApi.getConsigneeName());
		receiver.setCompany(expressOrderApi.getConsigneeCompanyName());
		receiver.setPost_code(expressOrderApi.getConsigneePostCode());
		receiver.setPhone(expressOrderApi.getConsigneeTelephone());
		receiver.setMobile(expressOrderApi.getConsigneeMobile());
		receiver.setEmail(expressOrderApi.getConsigneeEmail());
		receiver.setNation(expressOrderApi.getCountryCode());
		receiver.setProvince(expressOrderApi.getConsigneeState());
		receiver.setCity(expressOrderApi.getConsigneeCity());
		receiver.setAddress(expressOrderApi.getConsigneeAddress());
		receiver.setLinker(expressOrderApi.getConsigneeName());
		List<OrderInvoice> orderInvoices = expressOrderApi.getOrderInvoices();
		List<Item> items = new ArrayList<Item>();
		Double totalWeight = 0D;
		Double contentsTotalValue = 0D;
		Item item = null;
		if (CollectionUtils.isNotEmpty(orderInvoices)) {
			for (OrderInvoice orderInvoice : orderInvoices) {
				item = new Item();
				item.setCargo_no(orderInvoice.getSkuCode());
				item.setCargo_name(orderInvoice.getSkuNameCn());
				item.setCargo_name_en(orderInvoice.getSkuNameEn());
				item.setCargo_type_name(orderInvoice.getSkuNameCn());
				item.setCargo_type_name_en(orderInvoice.getSkuNameEn());
				item.setCargo_quantity(orderInvoice.getInvoicePcs());
				item.setCost(orderInvoice.getInvoiceAmount());
				Double invoiceWeight = orderInvoice.getInvoiceWeight();
				if (null != invoiceWeight) {
					totalWeight += invoiceWeight;
					item.setCarogo_weight(invoiceWeight * 1000);
				}
				item.setCargo_description(orderInvoice.getSkuCode());
				item.setCargo_currency(currency);
				item.setUnit(apiCredentials.get(ApiCredential.EUB_DEFAULT_UNIT.getKey()));
				item.setCargo_value(orderInvoice.getInvoiceAmount());
				contentsTotalValue += orderInvoice.getInvoiceAmount() == null ? 0D : orderInvoice.getInvoiceAmount();
				items.add(item);
			}
		}
		if (null == weight) {
			weight = totalWeight;
		}
		if (null != weight) {
			weight = weight * 1000;
		}
		interWeight = weight.intValue();
		if (interWeight < 1) {
			interWeight = 1;
		}
		logisticsInterface.setWeight(interWeight);
		logisticsInterface.setContents_total_weight(interWeight);
		logisticsInterface.setDeclare_source("2");
		logisticsInterface.setDeclare_type(apiCredentials.get(ApiCredential.EUB_DECLARE_TYPE.getKey()));
		logisticsInterface.setContents_total_value(contentsTotalValue);
		logisticsInterface.setSender(sender);
		logisticsInterface.setReceiver(receiver);
		logisticsInterface.setItems(items);
		return new Gson().toJson(logisticsInterface);
	}

	@Override
	public ApiOrderResponse createOrder(ExpressOrderApi expressOrderApi) {
		try {
			String orderJson = mapping(expressOrderApi).replaceAll("&", " ");
			LOGGER.info("eub.shipment.request:{}", orderJson);
			Map<String, String> apiCredentials = expressOrderApi.getApiCredentials();
			String verifyCode = orderJson + apiCredentials.get(ApiCredential.EUB_DATA_DIGEST.getKey());
			Map<String, String> params = new HashMap<String, String>();
			params.put("logistics_interface", orderJson);
			params.put("data_type", "JSON");
			params.put("data_digest", base64MessageDigest(verifyCode));
			params.put("msg_type", "B2C_TRADE");
			params.put("version", "1.0");
			params.put("ecCompanyId", apiCredentials.get(ApiCredential.EUB_SENDER_NO.getKey()));
			HttpClientResult httpClientResult = HttpClientPoolUtil.doPost(ApiCredential.EUB_ORDER_RECEIVE_BACK.getValue(), setDefaultHeader(), params, true);
			if (null == httpClientResult || httpClientResult != null && StringUtils.isBlank(httpClientResult.getContent())) {
				return new ApiOrderResponse(false, null, ApiOrderError.SERVICE_NO_DATA_RETURN.getMessage(), true, null);
			}
			String response = httpClientResult.getContent();
			LOGGER.info("eub.shipment.response:{}", response);
			// {\"success\":\"true\",\"waybillNo\":\"LW826176426CN\"}
			Map<String, String> responseMap = new Gson().fromJson(response, new TypeToken<Map<String, String>>() {
			}.getType());
			if (MapUtils.isEmpty(responseMap)) {
				return new ApiOrderResponse(false, null, ApiOrderError.SERVICE_NO_DATA_RETURN.getMessage(), true, response);
			}
			String success = responseMap.get("success");
			if (DEFAULT_SUCCESS.equalsIgnoreCase(success)) {
				String trackingNumber = responseMap.get("waybillNo");
				ApiOrderResponse apiOrderResponse = new ApiOrderResponse(true, trackingNumber, response);
				apiOrderResponse.setReferenceNo(expressOrderApi.getCustomerInvoiceCode());
				ApiLabelResponse apiLabelResponse = label(trackingNumber, apiCredentials);
				if (StringUtils.isNotBlank(apiLabelResponse.getLabelPath())) {
					apiOrderResponse.setLabelUrl(apiLabelResponse.getLabelPath());
					apiOrderResponse.setLmLabelUrl(apiLabelResponse.getRelativePath());
				}
				return apiOrderResponse;
			}
			return new ApiOrderResponse(false, null, responseMap.get("msg"), response);
		} catch (Exception e) {
			LOGGER.error("eub.shipment.exception:{}", e);
			return new ApiOrderResponse(false, null, ApiOrderError.CREATE_ORDER_EXCEPTION.getMessage());
		}
	}

	@Override
	public ApiLabelResponse label(String barCode, Map<String, String> configureMap) {
		Gson gson = new Gson();
		String mainBarCode = "";
		if (barCode.indexOf(",") > -1) {
			String[] barCodes = barCode.split(",");
			mainBarCode = barCodes[0];// 多个追踪号取首个作为文件名
		}
		try {
			Map<String, String> paramsMap = new HashMap<String, String>();
			paramsMap.put("ecCompanyId", configureMap.get(ApiCredential.EUB_SENDER_NO.getKey()));
			paramsMap.put("barCode", barCode);
			paramsMap.put("version", "2.0");
			paramsMap.put("pageType", "RM");
			String verifyCode = barCode + configureMap.get(ApiCredential.EUB_DATA_DIGEST.getKey());
			paramsMap.put("dataDigest", base64MessageDigest(verifyCode));
			HttpClientResult httpClientResult = HttpClientPoolUtil.doPost(ApiCredential.EUB_LABEL_PRODUCT.getValue(), setDefaultHeader(), paramsMap, true);
			if (null == httpClientResult) {
				return new ApiLabelResponse(barCode, null);
			}
			String response = httpClientResult.getContent();
			if (StringUtils.isBlank(response)) {
				return new ApiLabelResponse(barCode, null);
			}
			Map<String, Object> responseMap = gson.fromJson(response, new TypeToken<Map<String, Object>>() {
			}.getType());
			Boolean success = (Boolean) responseMap.get("success");
			// 当前月份
			String centrePath = ApiUtil.getCurrentDay() + File.separator;
			String relativePath = DEFAULT_LABEL_FOLDER + File.separator + centrePath;
			String folderPath = DEFAULT_LABEL_ROOT + relativePath;
			if (!new File(folderPath).exists()) {
				new File(folderPath).mkdirs();
			}
			if (Objects.equals(success, false)) {
				return new ApiLabelResponse(barCode, null);
			}
			String data = (String) responseMap.get("data");
			if (StringUtils.isBlank(data)) {
				return new ApiLabelResponse(barCode, null);
			}
			byte[] pdfBytes = HexUtil.hexStr2Bytes(data);
			String pdfName = (StringUtils.isNotBlank(mainBarCode) ? mainBarCode : barCode) + LabelSuffix.PDF.getSuffix();
			String fileUrl = folderPath + pdfName;
			FileUtils.writeByteArrayToFile(new File(fileUrl), pdfBytes);
			ApiLabelResponse apiLabelResponse = new ApiLabelResponse();
			apiLabelResponse.setBarCode(barCode);
			apiLabelResponse.setLabelPath(null);
			apiLabelResponse.setRelativePath(relativePath + pdfName);
			apiLabelResponse.setLabelFormat(LabelFormat.PDF.getFormat());
			apiLabelResponse.setLabelSize(LabelSize.LABEL_10_10.getSize());
			return apiLabelResponse;
		} catch (Exception e) {
			LOGGER.info("eub.label.exception:{}", e.getMessage());
			return new ApiLabelResponse(barCode, null);
		}
	}

	@Override
	public ApiLabelResponse labelRender(String trackNumber, ExpressOrderApi expressOrderApi) {
		return null;
	}

	@Override
	public void tracking(String trackNumber, Map<String, String> configureMap) {

	}

	@Override
	public String trackNumber(String code, Map<String, String> configureMap) {
		return null;
	}

	/**
	 * MessageDigest
	 * 
	 * @param messageDigest
	 * @return
	 * @author Henry(fba02)
	 * @version [版本号, Nov 5, 2019]
	 * @see [类、类#方法、类#成员]
	 */
	public static String base64MessageDigest(String messageDigest) {
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			md5.update(messageDigest.getBytes("UTF-8"));
			byte[] verifyCodeMd5 = md5.digest();
			// final java.util.Base64.Decoder decoder = java.util.Base64.getDecoder();
			final Base64.Encoder encoder = Base64.getEncoder();
			return encoder.encodeToString(verifyCodeMd5);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * Header
	 * 
	 * @return
	 * @author Henry(fba02)
	 * @version [版本号, Nov 5, 2019]
	 * @see [类、类#方法、类#成员]
	 */
	public static Map<String, String> setDefaultHeader() {
		Map<String, String> headers = new HashMap<String, String>();
		headers.put("Content-type", "application/x-www-form-urlencoded");
		return headers;
	}

	/**
	 * MD5字符串
	 * 
	 * @param originalStr
	 * @return
	 * @author Henry(fba02)
	 * @version [版本号, 2019年11月19日]
	 * @see [类、类#方法、类#成员]
	 */
	public static String MD5(String originalStr) {
		String md5Str = null;
		String base64Str = null;
		if (StringUtils.isBlank(originalStr)) {
			return null;
		}
		try {
			MessageDigest messageDigest = MessageDigest.getInstance("MD5");
			try {
				messageDigest.update(originalStr.getBytes("UTF-8"));
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			byte b[] = messageDigest.digest();
			StringBuffer buf = new StringBuffer();
			for (int offset = 0; offset < b.length; offset++) {
				int i = b[offset];
				if (i < 0) {
					i += 256;
				}
				if (i < 16) {
					buf.append("0");
				}
				buf.append(Integer.toHexString(i));
			}
			// 32位
			md5Str = buf.toString();
			// 转成base64位
			try {
				final Base64.Encoder encoder = Base64.getEncoder();
				base64Str = encoder.encodeToString(md5Str.getBytes("UTF-8"));
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return base64Str;
	}

	@Override
	public ApiLabelResponse label(String referenceNo, String trackNumber, Map<String, String> configureMap) {
		return null;
	}
}